/**
 * Driver.java
 * Autores: Jennifer Valdez 10189
 *          Gustavo Sánchez 10313
 *          Sergio Molina   10256
 * Fecha de entrega: 21 de noviembre de 2011
 * Descripcion: Es la clase principal que pide la expresion al usuario y lo interpreta por la clase list.
 * 
 */

//librerias importadas
import java.util.HashMap;
import java.util.Scanner;

public class Driver {

    /**
     * Programa Principal del interprete Lisp
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        
        //variables
        Scanner readln = new Scanner(System.in);    // para leer 
        boolean salir = false;                      // el usuario selecciono salir del programa
        String  resultado;                          
        HashMap funciones = new HashMap();          // posee todas las funciones declaradas por el usuario
        String ingreso;                             // lee el ingreso del usuario
        String ingreso2;
        String[] ingresoFinal;                      // expresion separada en atomos
        
        do{   //repetir mientras el usuario no ingrese (exit)
            instrucciones();
            int parentesis = 0;             // para evaluar la cantidad de parentesis
            boolean error = false;
            ingreso = readln.nextLine();
            ingreso2 = "";
            ingreso = ingreso.toUpperCase();    // se convierte todo a mayusculas

            //se revisa que exista la misma cantidad de parentesis abiertos y cerrados
            for (int i = 0; i< ingreso.length(); i++){
                if (ingreso.charAt(i) == '(')
                    parentesis ++;
                if (ingreso.charAt(i) == ')')
                    parentesis --;
            }

            error = (parentesis != 0);

            if (error)
                System.out.println("La cantidad de parentesis esta incorrecta");
            else{
                ingresoFinal=ingreso.split(" ");        // separa el String
                boolean finished;
                boolean defun=false;
                do{
                    finished=true;
                    for(int x=0; x<ingresoFinal.length; x++){
                        if(!ingresoFinal[x].isEmpty()){
                            // verifica si se esta declarando una funcion
                            if((ingresoFinal[x].equals("DEFUN"))||((ingresoFinal[x].equals("(DEFUN")))){
                                defun=true;
                                ingresoFinal[x+1]=ingresoFinal[x+1].replace("(", " ( ");
                            }
                            if((ingresoFinal[x].contains("(")))
                                ingreso2+=ingresoFinal[x].replace("(", " ( ")+" ";
                            else
                                ingreso2+=ingresoFinal[x].replace(")", " ) ")+" ";
                        }
                    }

                    //se vuelve a dividir la expresion separando mas sus componentes
                    ingresoFinal=ingreso2.split(" ");
                    for(int y=0;y<ingresoFinal.length; y++){
                        if((ingresoFinal[y].indexOf("(")!=-1)&&(ingresoFinal[y].length()>1))
                            finished=false;
                        if((ingresoFinal[y].indexOf(")")!=-1)&&(ingresoFinal[y].length()>1))
                            finished=false;

                    }
                    ingreso2=null;
                    ingreso2="";
                }while(!finished); // repetir hasta que termine
                int cont=0;
                for(int x=0; x<ingresoFinal.length;x++){    
                    if(!ingresoFinal[x].isEmpty()){
                        cont++;
                    }
                }
                String[] instrucciones=new String[cont];
                cont=0;
                // se eliminan las casillas vacias
                for(int x=0; x<ingresoFinal.length;x++){    
                    if(!ingresoFinal[x].isEmpty()){
                        instrucciones[cont]=ingresoFinal[x];
                        cont++;
                    }
                }
                if(defun){ // si es funcion
                    int indexargs=0;
                    String[] dec;       // vector de string con la declaracion
                    String[] body;      // vector de string con el cuerpo de la funcion
                    for (int x=0;x<instrucciones.length;x++){
                        if(instrucciones[x].equals(")")){
                            indexargs=x;    // determina cuantos parametros posee la funcion
                            x=instrucciones.length;
                        }
                    }
                    
                    indexargs++;
                    dec=new String[indexargs-2];     // inicializa la declaracion de una funcion

                    for (int z=2;z<indexargs;z++)
                        dec[z-2]=instrucciones[z];  //Copia a dec la declaracion de la funcion en el formato nombre(parametros)
                    error=false;
                    //verifica que ni el nombre ni los parametros empiecen con nada mas que con letras
                    if ((dec[0].charAt(0)<'A') || (dec[0].charAt(0)>'Z'))
                        error = true;
                    if(!error)
                        for(int z=2;z<dec.length-1;z++)
                            if ((dec[z].charAt(0)<'A') || (dec[z].charAt(0)>'Z')) 
                                error = true;
                    if((!dec[1].equals("(")) || (!dec[dec.length-1].equals(")")))
                        error=true;
                    
                    //Verifica si no hubo ningun error de declaracion
                    if(!error){
                    body=new String[instrucciones.length-indexargs];     //Inicializa la variable body
                    for (int z=indexargs;z<instrucciones.length-1;z++){
                        body[z-indexargs]=instrucciones[z];             //Copia a body todo el cuerpo de la funcion
                    }
                    Funcion funcionNueva = new Funcion(dec, body);      //Crea un nuevo objeto funcion con la declaracion dec y cuerpo body
                    funciones.put(funcionNueva.getName(), funcionNueva);//Se incluye la nueva funcion en el hashmap de funciones declaradas
                    System.out.println(funcionNueva.getName());
                    }else{
                        System.out.println("Funcion mal declarada");    //Indica si hubo un error en la declaracion
                    }
                }
                else{
                    List lista = new List(instrucciones, funciones);    //Se crea un nuevo objeto funciones donde se actualizan las funciones actuales en el hashmap
                    resultado = lista.evaluar();                        //Se llama a evaluar para tener el resultado de las instrucciones que ingreso el usuario
                    if (resultado.equals("(EXIT)"))
                        salir = true;
                    else
                        System.out.println("->  "+resultado);
                }
             }
        }while (!salir);
                
    }
    
    
    /**
     * muestra las instrucciones
     */
    public static void instrucciones(){
        System.out.println("\n+--  INSTRUCCIONES   -------------------------------+");
        System.out.println("| Ha ingresado a la simulacion de un interprete de  |");
        System.out.println("| LISP, introduzca cada funcion siguiendo la        |");
        System.out.println("| sintaxis de LISP, utilice prefix y parentesis para|");
        System.out.println("| cada subfuncion. Para salir introduzca (exit).    |");
        System.out.println("+---------------------------------------------------+\n");
    }
}
